/*
 * ux_x11.c: fetch local auth data for X forwarding.
 */

#include <ctype.h>
#include <unistd.h>
#include <assert.h>
#include "putty.h"
#include "ssh.h"

void platform_get_x11_auth(char *display, int *protocol,
                           unsigned char *data, int *datalen)
{
    FILE *fp;
    char *command;
    int maxsize = *datalen;
    char *localbuf;
    int proto = -1;

    display = x11_display(display);
    /*
     * Normally we should run `xauth list DISPLAYNAME'. However,
     * there's an oddity when the display is local: the display
     * `localhost:0' (or `:0') should become just `:0'.
     */
    if (!strncmp(display, "localhost:", 10)
	|| !strncmp(display, "unix:", 5))
	command = dupprintf("xauth list %s 2>/dev/null",
			    strchr(display, ':'));
    else
	command = dupprintf("xauth list %s 2>/dev/null", display);
    sfree(display);
    fp = popen(command, "r");
    sfree(command);

    if (!fp)
        return;                        /* assume no auth */

    localbuf = snewn(maxsize, char);

    while (1) {
        /*
         * Read a line from stdin, and attempt to parse it into a
         * display name (ignored), auth protocol, and auth string.
         */
        int c, i, hexdigit;
        char protoname[64];

        /* Skip the display name. */
        while (c = getc(fp), c != EOF && c != '\n' && !isspace(c));
        if (c == EOF) break;
        if (c == '\n') continue;

        /* Skip white space. */
        while (c != EOF && c != '\n' && isspace(c))
            c = getc(fp);
        if (c == EOF) break;
        if (c == '\n') continue;

        /* Read the auth protocol name, and see if it matches any we
         * know about. */
        i = 0;
        while (c != EOF && c != '\n' && !isspace(c)) {
            if (i < lenof(protoname)-1) protoname[i++] = c;
            c = getc(fp);
        }
        protoname[i] = '\0';

        for (i = X11_NO_AUTH; ++i < X11_NAUTHS ;) {
            if (!strcmp(protoname, x11_authnames[i]))
                break;
        }
        if (i >= X11_NAUTHS || i <= proto) {
            /* Unrecognised protocol name, or a worse one than we already have.
	     * Skip this line. */
            while (c != EOF && c != '\n')
                c = getc(fp);
            if (c == EOF) break;
        }
        proto = i;

        /* Skip white space. */
        while (c != EOF && c != '\n' && isspace(c))
            c = getc(fp);
        if (c == EOF) break;
        if (c == '\n') continue;

        /*
         * Now grab pairs of hex digits and shove them into `data'.
         */
        i = 0;
        hexdigit = -1;
        while (c != EOF && c != '\n') {
            int hexval = -1;
            if (c >= 'A' && c <= 'F')
                hexval = c + 10 - 'A';
            if (c >= 'a' && c <= 'f')
                hexval = c + 10 - 'a';
            if (c >= '0' && c <= '9')
                hexval = c - '0';
            if (hexval >= 0) {
                if (hexdigit >= 0) {
                    hexdigit = (hexdigit << 4) + hexval;
                    if (i < maxsize)
                        localbuf[i++] = hexdigit;
                    hexdigit = -1;
                } else
                    hexdigit = hexval;
            }
            c = getc(fp);
        }

        *datalen = i;
        *protocol = proto;
	memcpy(data, localbuf, i);

	/* Nonetheless, continue looping round; we might find a better one. */
    }
    pclose(fp);
    sfree(localbuf);
}
